<template>
  <div class="call-bar-container">
    <template v-for="btnCode in callBarButtonList">
      <SignInOrOut v-if="btnCode==='sign'" :key="btnCode" />
      <!-- 签入签出不能为灰色 -->
      <Skill v-if="btnCode==='skill'" :key="btnCode" />
      <SendMessage v-if="btnCode==='send_message'" :key="btnCode" />
      <Listen v-if="btnCode==='listen'" :key="btnCode" />
      <Free v-if="btnCode==='free'" :key="btnCode" />
      <!-- <OnlineOrOffline /> -->
      <CallOutBtn v-if="btnCode==='call_out'" :key="btnCode" />
      <Queue v-if="btnCode==='queue'" :key="btnCode" />
      <Answer v-if="btnCode==='answer'" :key="btnCode" />
      <Transfer v-if="btnCode==='transfer'" :key="btnCode" />
      <Hold v-if="btnCode==='hold'" :key="btnCode" />
      <Mute v-if="btnCode==='mute'" :key="btnCode" />
      <Evaluate v-if="btnCode==='evaluate'" :key="btnCode" />
      <Release v-if="btnCode==='release'" :key="btnCode" />
    </template>

    <ul v-show="agentState.talk" class="call-bar-tel">
      <li>
        <span>电话号码：</span>
        <span>
          {{ callInfo.realCallOutTel || callInfo.callTel }}
        </span>
        <!-- <span>{{ callAddress }}</span> -->
        <span></span>
      </li>
      <li>
        <span>通话时长：</span>
        <span>{{ callInfo.duration }}</span>
      </li>
    </ul>
  </div>
</template>

<script>
import SignInOrOut from './components/SignInOrOut'
import Skill from './components/Skill'
import Free from './components/Free'
// import OnlineOrOffline from './components/OnlineOrOffline'
import Answer from './components/Answer'
import Release from './components/Release'
import CallOutBtn from './components/CallOut'
import Transfer from './components/Transfer'
import Mute from './components/Mute'
import Queue from './components/Queue'
import Listen from './components/Listen'
import Hold from './components/Hold'
import Evaluate from './components/Evaluate'
import SendMessage from './components/SendMessage'
import { mapMutations, mapGetters, mapActions } from 'vuex'
import { setStore, getStore } from '@/utils/store'
import { postCreate, putCreate, releaseCompanyPhone } from '@/api/telRecord'
import { getValueByChain } from '@/utils'
import { postCallOperateLog } from '@/api/callOperateLog'
import { delPrefixUseRecord } from '@/api/prefixUseRecord'
import { putPrefixNumberInfoIsUsed } from '@/api/prefixNumberInfo'
import Vue from 'vue'
export default {
  name: 'CallBar',
  components: {
    SignInOrOut,
    Skill,
    Free,
    // OnlineOrOffline,
    Answer,
    Release,
    CallOutBtn,
    Transfer,
    Mute,
    Queue,
    Listen,
    Hold,
    Evaluate,
    SendMessage
  },
  data() {
    return {}
  },
  computed: {
    ...mapGetters([
      'callInfo',
      'agentState',
      'interval',
      'skillList',
      'queueSize',
      'agentid',
      'phonenum',
      'agentInfo',
      'userCache',
      'staffName',
      'firstDept',
      'callBarButtonList',
      'menuTree',
      'visitedViews',
      'recordList',
      'accountNo',
      'callAddress',
      'startCallTime',
      'staffBusinessOpportunitySources'
    ])
  },
  watch: {
    // 监听标签变化，当来电弹屏关闭时，从vuex删除对应记录
    // visitedViews() {
    //   const callids = Object.keys(this.recordList)
    //   const callid = this.callInfo.callid || null
    //   const tags = this.visitedViews.map(e => e.query ? e.query.callid : 'null')
    //   for (const c of callids) {
    //     if (!tags.includes(c)) {
    //       if (this.callInfo.callid !== callid) {
    //         this.SET_RECORD_LIST(c)
    //       }
    //     }
    //   }
    // }
  },
  created() {
    // 重新设置bus防止退出登录后在登陆多次注册事件
    if (this.$cps.bus) {
      try {
        this.$cps.bus.$destroy()
      } catch (err) {
        console.log(err)
      }
    }
    this.$cps.bus = new Vue()
    this.register()
    // 尝试从本地获取phonenum
    if (!this.phonenum) {
      const pn = getStore({ name: 'phonenum' })
      if (pn) {
        this.SET_PHONE_NUM(pn)
        localStorage.removeItem('phonenum')
      }
    }
    window.onbeforeunload = () => {
      // 解决刷新后phonenum丢失
      if (this.phonenum) {
        setStore({ name: 'phonenum', content: this.phonenum })
      }
    }
    // 将话务流水提交方法绑定到vue原型
    const p = Vue.prototype
    p.$callOperatorLog = this.callOperatorLog
    p.$callOperatorLogSuccess = this.callOperatorLogSuccess
    p.$callOperatorLogFail = this.callOperatorLogFail
  },
  methods: {
    ...mapMutations([
      'SET_AGENT_ID',
      'SET_PHONE_NUM',
      'SET_CALL_ID',
      'SET_CALL_TEL',
      'SET_REAL_CALL_OUT_TEL',
      'SET_CALL_DATA',
      'SET_AGENT_INFO',
      'SET_AGENT_STATE',
      'SET_SKILL_LIST',
      'SET_QUEUE_SIZE',
      'SET_DURATION',
      'SET_RECORD_LIST',
      'SET_CLEAR_DATA',
      'SET_IS_CALL_OUT',
      'SET_CALL_ADDRESS',
      'SET_CALL_OUT_RECORD_ID',
      'SET_CALL_PREFIX',
      'SET_STATE_CALL_TIME'
    ]),
    ...mapActions(['computedDuration', 'stopDuration', 'callerInfo']),
    // 注册改变坐席状态的事件
    register() {
      const on = this.$cps.on.bind(this.$cps)
      const setTrue = this.setTrue
      const setFalse = this.setFalse
      // 通话时长相关
      on(
        [
          'OnCallStateWork',
          'OnSayFreeSuccess',
          'OnWorkSuccess',
          'OnPhoneRelease'
        ],
        this.stopDuration
      )
      on(['OnRecordSuccess', 'OnRecordFailure'], () => {
        this.computedDuration()
      })
      // 获取通话信息
      on(['OnCallRinging', 'OnCallTalking', 'OnAutoAnswer'], (res) => {
        this.updateCallInfo(res)
      })
      // 清除通话信息 'OnCancelWorkSuccess'
      on(['OnSayFreeSuccess'], () => {
        this.SET_DURATION('00:00:00')
        this.SET_CALL_ID('')
        this.SET_CALL_TEL('')
        this.SET_REAL_CALL_OUT_TEL('')
        this.$cps.setCallId('')
        this.SET_CALL_ADDRESS('')
        this.SET_CALL_OUT_RECORD_ID('')
        this.SET_CALL_PREFIX('')
        this.$cps.callData = {} // 必须清空，不然会污染下一次的通话数据
        this.SET_STATE_CALL_TIME(null) // 清空开始通话时间
      })

      // 签入签出
      on('OnSignInSuccess', setTrue('sign'))
      on('OnSignOutSuccess', () => {
        setFalse('sign')()
        this.$cps.stopPolling()
        // 签出后清理数据
        this.SET_CLEAR_DATA()
      })
      // 示忙、示闲
      on('OnSayFreeSuccess', setTrue('free'))
      on('OnForceIdle', setTrue('free'))
      on('OnBusy', () => {
        setFalse('free')()
        this.SET_STATE_CALL_TIME() // 记录通话开始时间
      })
      on('OnSayBusySuccess', setFalse('free'))
      on('OnCancelWorkSuccess', setTrue('free')) // 退出工作状态
      on('OnCallStateWork', setFalse('free')) // 进入工作状态
      // 工作中
      on('OnWorkSuccess', setTrue('work'))
      on('OnCallStateWork', setTrue('work'))
      on('OnCancelWorkSuccess', setFalse('work'))
      // 坐席响铃
      on('OnPhoneAlerting', () => {
        this.$notifyInfo('话机振铃，请及时接听！')
      }) // 物理座机响铃
      on(['OnCallRinging', 'OnPhoneAlerting'], setTrue('alert')) // 提醒坐席需要手工应答
      on(['OnLongNoAnswer',
        'OnPhoneRelease',
        'OnAutoAnswer',
        'OnNoAnswerFromCti',
        'OnCallTalking',
        'OnSayFreeSuccess',
        'OnForceIdle',
        'OnCallRelease',
        'OnCustomerRelease',
        'OnCallStateWork',
        'OnStartPlaySuccess',
        'OnPlaySuccess',
        'OnPhoneOffhook' // 监听
      ], setFalse('alert'))
      // 取消呼出状态
      on(['OnCallRelease', 'OnSayFreeSuccess', 'OnForceIdle'], () => {
        this.SET_IS_CALL_OUT(false)
      })
      // 应答
      on('OnCallTalking', setTrue('talk'))
      on('OnCallRelease', setFalse('talk'))
      on('OnCustomerRelease', setFalse('talk'))
      // 保持
      on('OnHoldSuccess', setTrue('hold'))
      on(['OnCallTalking', 'OnSayFreeSuccess'], setFalse('hold'))
      // 静音
      on('OnBeginMute', setTrue('mute')) // 自定义事件，在请求成功后触发
      on('OnEndMute', setFalse('mute')) // 自定义事件，在请求成功后触发
      on('OnSayFreeSuccess', setFalse('mute'))
      // 三方通话
      on('OnCallConference', setTrue('mpty'))
      on('OnCustomerRelease', setFalse('mpty'))
      // 外呼相关提醒
      on('OnCallOutFailure', () => {
        this.$notifyError('外呼失败！')
      })
      // 对方振铃
      on('OnCustomerAlerting', () => {
        // this.$notifyInfo('对方振铃！')
      })
      // 来电弹屏，只有呼入才有来电弹屏，呼出没有OnCallRinging事件
      // 自动应答呼入没有 OnCallRinging
      on(['OnCallRinging', 'OnAutoAnswer'], () => {
        this.SET_REAL_CALL_OUT_TEL('')
        // 创建通话记录并获取随路数据,将通话记录id返回给弹屏
        this.$nextTick(() => {
          let callInfoByCallId
          // 先获取随路数据, 因为转移时首先获取到的是转移坐席号码
          Promise.all([this.getCallData(), this.getCallInfo()]).then(resps => {
            try {
              callInfoByCallId = resps[1].data
            } catch (error) {
              callInfoByCallId = undefined
              console.log(error)
            }
          }).catch(() => {
            callInfoByCallId = undefined
          }).finally(() => {
            const ci = this.callInfo
            const callid = ci.callid
            const callTel = this.$prefixTrim(ci.callTel, ci.prefix)
            // 先更新通话记录列表
            this.SET_RECORD_LIST({
              callid,
              callTel
              // startRecordTime: new Date().getTime() // 通话开始时间
            })
            // 创建记录
            this.createTelRecord(callInfoByCallId).then(recordId => {
              // 通话记录创建成功则将通话记录id带在查询参数上
              this.$router.push({
                name: 'Display',
                query: {
                  callid,
                  callTel
                }
              })
            }).catch(() => {
              // 通话记录创建失败，手动弹屏时需要创建通话记录
              this.$router.push({
                name: 'Display',
                query: {
                  callid,
                  callTel
                }
              })
            })
          })
          // 获取随路数据
          // this.getCallData()
        })
      })
      // 记录通话时长等信息
      on(['OnCallRelease'], this.stopRecord)
      // 释放公司号码资源
      // on(['OnCallRelease'], this.releaseCompanyPhoneResource)
      // 外呼失败
      on('OnCallOutFailure', this.callOutFail)
      // 释放公司号码资源
      // on('OnCallOutFailure', this.releaseCompanyPhoneResource)
      // 记录录音文件地址
      on([`OnRecordSuccess`, `OnRecordFailure`], res => {
        this.recordFileAddress(res)
      })
    },
    // 修改坐席状态
    // true
    setTrue(key) {
      return () => {
        this.SET_AGENT_STATE({ key, value: true })
      }
    },
    // false
    setFalse(key) {
      return () => {
        this.SET_AGENT_STATE({ key, value: false })
      }
    },
    // 创建通话记录
    createTelRecord(callInfoByCallId) {
      let callTel = this.callInfo.callTel || ''
      const callid = this.callInfo.callid
      callTel = this.$prefixTrim(callTel, this.callInfo.prefix)
      const callData = this.callInfo.callData || {}
      // 呼入被叫号码
      const called = (callInfoByCallId || {}).called
      const data = {
        callSerialNumber: this.callInfo.callid,
        customerPhone: callTel,
        callType: 1, // 呼叫类型 1:呼入，2：普通呼出，3：未接呼出，4：外呼任务呼出，5：呼入转接 0：全部
        caller: this.agentid,
        callerName: this.staffName,
        department: this.firstDept,
        talkTime: 0,
        transfer: 0, // 转接类型 0未转接，1转出方，2转入方
        evaluateType: 0,
        ivrOption: callData.zrcdbh || null,
        localNumber: called || callData.called || '', // 呼入被叫号码
        callSource: this.$getCallSource() // 通话来源
      }
      // 判断是否是转移过来的电话
      if (this.callInfo.transferId) {
        data.transferId = this.callInfo.transferId
        data.transfer = 2
      }
      return postCreate(data).then(res => {
        // 更新记录id
        this.SET_RECORD_LIST({
          callid,
          recordId: res.data.id
        })
        return res.data.id
      })
    },
    // 获取随路数据
    getCallData() {
      return this.$cps.queryCallData().then(res => {
        const callData = res.data.calldata
        const callTel = callData.callTel || callData.CustomPhoneNo || this.callInfo.callTel // 电话号码以随路数据为准
        if (callTel) {
          this.SET_CALL_TEL(callTel)
        }
        const d = {
          callid: this.callInfo.callid,
          transferId: callData.recordId || '', // 转移过来的电话上一条记录id
          callData
        }
        if (callTel) {
          d.callTel = callTel
        }
        this.SET_RECORD_LIST(d)
        return res
      })
    },
    // 获取呼叫信息,更新通话记录 localNumber
    getCallInfo() {
      const callid = this.callInfo.callid
      return this.$cps.queryCallInfoByCallId(callid).then(res => {
        const d = {
          callid: this.callInfo.callid,
          callInfoByCallId: res.data || {}
        }
        this.SET_RECORD_LIST(d)

        const callInfo = this.recordList[this.callInfo.callid] || {}
        // 呼入被叫号码
        if (callInfo.callData && !callInfo.callData.called) {
          callInfo.callData.called = res.data.called
        }
        return res
      })
    },
    // 更新通话信息
    updateCallInfo(res) {
      const content = getValueByChain(res, 'data.content', {})
      const callData = getValueByChain(res, 'data.callData', {})
      const recordId = getValueByChain(callData, 'recordId', '') // 存在则表示是转移
      this.SET_CALL_ID(content.callid)
      this.$cps.setCallId(content.callid) // 更新callid用于单个通话记录事件
      let caller = getValueByChain(content, 'caller', '')
      let called = getValueByChain(content, 'called', '')
      const feature = getValueByChain(content, 'feature', '')
      const info = {
        callid: content.callid
      }
      if (recordId) {
        this.SET_CALL_TEL(callData.callTel)
        info.callTel = callData.callTel
      } else {
        if (feature === '7') {
          called = this.$prefixTrim(called, this.callInfo.prefix)
          if (called) {
            this.SET_CALL_TEL(called)
            info.callTel = called
          }
        } else {
          caller = this.$prefixTrim(caller, this.callInfo.prefix)
          if (called) {
            this.SET_CALL_TEL(caller)
            info.callTel = caller
          }
        }
      }
      // 记录通话
      this.SET_RECORD_LIST(info)
    },
    // 监听录音事件，更新通话记录录音文件地址
    // 保存录音地址,录音开始时间
    recordFileAddress(res) {
      const c = res.data?.content ?? {}
      this.SET_RECORD_LIST({
        callid: this.callInfo.callid,
        startRecordTime: new Date().getTime(),
        recordingAddress: c.fileName
      })

      // 保存录音文件
      const id = (this.recordList[this.callInfo.callid] || {}).recordId
      const data = {
        id,
        recordingAddress: c.fileName || null
      }
      putCreate(data).catch(e => {
        console.log('保存录音地址失败！', this.callInfo.callTel)
      })
    },
    // 停止录音保存通话时长
    stopRecord() {
      // 咨询转移的话还有两次OnCallRelease事件
      // 外呼时
      const callInfo = this.recordList[this.callInfo.callid] || {}
      // const cd = callInfo.callData || {}
      // if (cd.type === 'consult' && cd.goOut) {
      //   this.$cps.once('OnCallRelease', this.stopRecord)
      //   cd.goOut = false
      // }
      // 通话接收回调
      try {
        callInfo.callback('success', callInfo)
      } catch (error) {
        // console.log(error)
      }
      // 弹屏时
      // if (cd.type === 'consult' && cd.display) {
      //   this.$cps.once('OnCallRelease', this.stopRecord)
      //   cd.display = false
      // }
      const endRecordTime = new Date().getTime()
      // const startRecordTime = this.startCallTime || callInfo.startRecordTime || endRecordTime
      const startRecordTime = callInfo.startRecordTime || endRecordTime
      let talkTime = 0
      try {
        talkTime = Math.floor((endRecordTime - startRecordTime) / 1000)
      } catch (error) {
        console.log(error, '通话时长计算失败')
      }
      const data = {
        talkTime,
        id: callInfo.recordId,
        recordingAddress: callInfo.recordingAddress || null,
        ivrOption: callInfo.zrcdbh || null
      }
      // 判断是否是转移过来的电话
      if (callInfo.transferId) {
        data.transferId = callInfo.transferId
        data.transfer = 2
      }
      // 呼入被叫号码
      const _called = (callInfo.callInfoByCallId || {}).called || (callInfo.callData || {}).called
      if (_called) {
        data.localNumber = _called
      }
      putCreate(data).catch(e => {
        console.log('保存通话时长失败！', this.callInfo.callTel)
      })

      // 前缀相关
      this.putPrefixNumberInfoIsUsed(callInfo)
    },
    // 释放公司号码资源
    releaseCompanyPhoneResource() {
      const callInfo = this.recordList[this.callInfo.callid] || {}
      const companyPhone = (callInfo.callInfoByCallId || {}).called || (callInfo.prefixInfo || {}).phoneNumber
      if (!companyPhone) {
        return Promise.reject('')
      }
      return releaseCompanyPhone(companyPhone).then(res => {
        console.log(res)
      })
    },
    // 呼叫是否失败 OnCallOutFailure
    callOutFail(v) {
      this.SET_REAL_CALL_OUT_TEL('')
      if (this.callInfo.callid !== (v.data.content || {}).callid) {
        // 通话失败回调
        try {
          const list = Object.values(this.recordList)
          list[list.length - 1].callback('failed', list[list.length - 1] || {})
        } catch (error) {
          // console.log(error)
        }
        return
      }
      const callInfo = this.recordList[this.callInfo.callid] || {}
      // 通话失败回调
      try {
        callInfo.callback('failed', callInfo)
      } catch (error) {
        // console.log(error)
      }
      // 计算时长
      const endRecordTime = new Date().getTime()
      // const startRecordTime = this.startCallTime || callInfo.startRecordTime || endRecordTime
      const startRecordTime = callInfo.startRecordTime || endRecordTime
      let talkTime = 0
      try {
        talkTime = Math.floor((endRecordTime - startRecordTime) / 1000)
      } catch (error) {
        console.log(error, '通话时长计算失败')
      }

      const data = {
        id: callInfo.recordId,
        talkTime,
        callType: 3,
        recordingAddress: callInfo.recordingAddress || null,
        ivrOption: callInfo.zrcdbh || null
      }

      putCreate(data).catch(e => {
        console.log('呼叫失败！修改呼叫类型失败！')
      })
      // 前缀相关
      this.putPrefixNumberInfoIsUsed(callInfo).finally(() => {
        this.delPrefixUseRecord(callInfo)
      })
    },
    // 删除前缀使用记录
    delPrefixUseRecord(callInfo) {
      const prefixUseId = callInfo.prefixInfo && callInfo.prefixInfo.prefixUseId
      if (prefixUseId) {
        delPrefixUseRecord(prefixUseId)
      }
    },
    // 修改前缀为未使用
    putPrefixNumberInfoIsUsed(callInfo) {
      const id = callInfo.prefixInfo && callInfo.prefixInfo.id
      if (id) {
        return putPrefixNumberInfoIsUsed(id, 0)
      }
      return Promise.reject('没有前缀使用记录id')
    },

    // 话务流水相关
    // 新增话务操作流水
    callOperatorLog(operateType, isSuccess, remark, operateDate) {
      const data = {
        // accountNo: this.accountNo, // string操作人
        extensionNumber: this.phonenum, //	string分机号
        isSuccess, //*	integer($int32)是否成功：0否1是
        // operateDate: parseTime(operateDate),	// 操作日期
        operateType,	// 操作类型：1签入2签出3示闲4示忙5转移6保持7静音8挂机9评价
        remark // 备注
      }
      postCallOperateLog(data).then(res => {
        console.info(`话务流水操作成功：${JSON.stringify(data)}`)
      }).catch(() => {
        console.info(`话务流水操作失败：${JSON.stringify(data)}`)
      })
    },
    // 操作成功
    callOperatorLogSuccess(operateType, remark = '', operateDate = new Date()) {
      this.callOperatorLog(operateType, 1, remark, operateDate)
    },
    // 操作失败
    callOperatorLogFail(operateType, remark = '', operateDate = new Date()) {
      this.callOperatorLog(operateType, 0, remark, operateDate)
    }
  }
}
</script>
<style lang="scss" scoped>
@import "@/styles/mixin.scss";
.call-bar-container {
  @include flex($align: center, $justify: flex-start);
  height: 100%;
  padding: 10px;
  box-sizing: border-box;
  & > * {
    height: 50px;
    width: 60px;
    @include flex($align: center, $justify: center);
  }
  .call-bar-tel {
    list-style: none;
    padding: 0;
    margin: 0;
    @include flex($wrap: wrap, $align: center);
    line-height: 1em;
    height: auto;
    & > * {
      @include flex($align: center);
      & > span {
        flex: 0 0 60px;
      }
      &>span:nth-of-type(3){
        padding-left: 5px;
        color: #fa8c16;
        font-size: 12px;
        align-self: flex-end;
      }
    }
    & > li:first-of-type {
      margin-bottom: 10px;
    }
  }
}
</style>
